/* * File ModelBuilder.java * * Copyright (C) 2010 Remco Bouckaert remco@cs.auckland.ac.nz * * This file is part of BEAST2. * See the NOTICE file distributed with this work for additional * information regarding copyright ownership and licensing. * * BEAST is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as * published by the Free Software Foundation; either version 2 * of the License, or (at your option) any later version. * * BEAST 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 Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with BEAST; if not, write to the * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, * Boston, MA 02110-1301 USA */ package beast.app.draw; //import com.itextpdf.awt.PdfGraphics2D; //import com.itextpdf.text.pdf.PdfContentByte; //import com.itextpdf.text.pdf.PdfWriter; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Cursor; import java.awt.Dimension; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.ComponentEvent; import java.awt.event.ComponentListener; import java.awt.event.InputEvent; import java.awt.event.KeyEvent; import java.awt.event.MouseAdapter; import java.awt.event.MouseEvent; import java.awt.event.MouseMotionAdapter; import java.awt.image.BufferedImage; import java.awt.print.PageFormat; import java.awt.print.Printable; import java.awt.print.PrinterException; import java.awt.print.PrinterJob; import java.io.File; import java.io.FileNotFoundException; import java.io.FileWriter; import java.io.IOException; import java.io.PrintStream; import java.util.ArrayList; import java.util.List; import javax.imageio.ImageIO; import javax.swing.Action; import javax.swing.JCheckBoxMenuItem; import javax.swing.JColorChooser; import javax.swing.JFileChooser; import javax.swing.JFrame; import javax.swing.JMenu; import javax.swing.JMenuBar; import javax.swing.JMenuItem; import javax.swing.JOptionPane; import javax.swing.JPanel; import javax.swing.JPopupMenu; import javax.swing.JScrollPane; import javax.swing.JToolBar; import javax.swing.KeyStroke; import javax.swing.UIManager; import javax.swing.filechooser.FileFilter; import beast.app.util.Utils; import beast.core.BEASTInterface; import beast.evolution.alignment.Sequence; import beast.util.AddOnManager; import beast.util.Randomizer; import beast.util.XMLProducer; /** * program for drawing BEAST 2.0 models * */ public class ModelBuilder extends JPanel implements ComponentListener { /** * for serialisation */ static final long serialVersionUID = 1L; /** * File extension for XML format containing all graphical details (colour, * position, etc) NB This is not the extension for GenerationD model files. */ static public final String FILE_EXT = ".xml"; final public static String ICONPATH = "beast/app/draw/icons/"; /** * contains the Panel */ JScrollPane m_jScrollPane; public DrawPanel g_panel; final static int MODE_SELECT = 0; final static int MODE_MOVE = 1; final static int MODE_ARROW = 7; final static int MODE_FUNCTION = 9; Shape m_drawShape = null; Rectangle m_selectRect = null; int m_nMode = MODE_SELECT; boolean m_bViewOperators = true; boolean m_bViewLoggers = true; boolean m_bViewSequences = true; boolean m_bViewState = true; // boolean m_bViewOperators = false; // boolean m_bViewLoggers = false; // boolean m_bViewSequences = false; // boolean m_bViewState = false; boolean m_bRelax = false; // flag to indicate the model can be edited. // set to false for read only view of the model, e.g. from beauti boolean m_bIsEditable = true; public void setEditable(boolean isEditable) { m_bIsEditable = isEditable; } /** * number of seconds to 'relax' after loading a file * */ int m_nRelaxSeconds = 10; /** * menu item indicating whether to relax or not * */ JCheckBoxMenuItem m_viewRelax; Action a_new = new ActionNew(); Action a_load = new ActionLoad(); Action a_save = new ActionSave(); Action a_saveas = new ActionSaveAs(); ActionExport a_export = new ActionExport(); ActionPrint a_print = new ActionPrint(); Action a_quit = new ActionQuit(); Action a_undo = new ActionUndo(); Action a_redo = new ActionRedo(); Action a_selectall = new ActionSelectAll(); Action a_delnode = new ActionDeleteNode(); Action a_cutnode = new ActionCutNode(); Action a_copynode = new ActionCopyNode(); Action a_pastenode = new ActionPasteNode(); Action a_group = new ActionCollapse(); // Action a_ungroup = new ActionUngroup(); Action a_select = new ActionSelect(); Action a_arrow = new ActionArrow(); Action a_function = new ActionFunction(); Action a_fillcolor = new ActionFillColor(); Action a_pencolor = new ActionPenColor(); Action a_tofront = new ActionToFront(); Action a_forward = new ActionForward(); Action a_toback = new ActionToBack(); Action a_backward = new ActionBackward(); Action a_alignleft = new ActionAlignLeft(); Action a_alignright = new ActionAlignRight(); Action a_aligntop = new ActionAlignTop(); Action a_alignbottom = new ActionAlignBottom(); Action a_centerhorizontal = new ActionCenterHorizontal(); Action a_centervertical = new ActionCenterVertical(); Action a_spacehorizontal = new ActionSpaceHorizontal(); Action a_spacevertical = new ActionSpaceVertical(); Action a_about = new ActionAbout(); Action a_relax = new ActionRelax(); Action a_viewOperators = new ActionViewOperators(); Action a_viewLoggers = new ActionViewLoggers(); Action a_viewSequences = new ActionViewSequences(); Action a_layout = new ActionLayout(); ClipBoard m_clipboard = new ClipBoard(); /** * name of current file, used for saving (as opposed to saveAs) * */ String m_sFileName = ""; /** * the document in the model/view pattern * */ public Document m_doc = new Document(); /** * current directory for opening files * */ String m_sDir = System.getProperty("user.dir"); public ModelBuilder() { } // GBDraw c'tor class ClipBoard { String m_sText = null; public ClipBoard() { if (a_pastenode != null) { a_pastenode.setEnabled(false); } } public boolean hasText() { return m_sText != null; } public String getText() { return m_sText; } public void setText(String text) { m_sText = text; a_pastenode.setEnabled(true); } } // class ClipBoard Selection m_Selection = new Selection(); ExtensionFileFilter ef1 = new ExtensionFileFilter(".xml", "BEAST files"); ExtensionFileFilter ef2 = new ExtensionFileFilter(".gif", "GIF images"); ExtensionFileFilter ef3 = new ExtensionFileFilter(".jpg", "JPG images"); ExtensionFileFilter ef4 = new ExtensionFileFilter(".bmp", "BMP images"); ExtensionFileFilter ef5 = new ExtensionFileFilter(".png", "PNG images"); boolean validateModel() { int status = m_doc.isValidModel(); if (status == Document.STATUS_OK) { return true; } String msg = "<html>Document is not valid: "; switch (status) { case Document.STATUS_CYCLE: msg += "there is a cycle in the model."; break; case Document.STATUS_EMPTY_MODEL: msg += "The model is empty, there is nothing to save."; break; case Document.STATUS_NOT_RUNNABLE: msg += "there is no top level runnable item in the model (e.g. an MCMC node)."; break; case Document.STATUS_ORPHANS_IN_MODEL: msg += "there are orphaned items in the model (i.e. beastObjects that have no parents)."; break; } msg += "<br>Do you still want to try to save the model?</html>"; if (JOptionPane.showConfirmDialog(this, msg, "Model not valid", JOptionPane.YES_NO_OPTION) == JOptionPane.YES_OPTION) { return true; } return false; } class ActionSave extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -20389110859355156L; public ActionSave() { super("Save", "Save Graph", "save", KeyEvent.VK_S); } // c'tor public ActionSave(String name, String toolTipText, String icon, int acceleratorKey) { super(name, toolTipText, icon, acceleratorKey); } // c'tor @Override public void actionPerformed(ActionEvent ae) { if (!m_sFileName.equals("")) { if (!validateModel()) { return; } saveFile(m_sFileName); m_doc.isSaved(); } else { if (saveAs()) { m_doc.isSaved(); } } } // actionPerformed boolean saveAs() { if (!validateModel()) { return false; } JFileChooser fc = new JFileChooser(m_sDir); fc.addChoosableFileFilter(ef1); fc.setDialogTitle("Save Model As"); if (!m_sFileName.equals("")) { // can happen on actionQuit fc.setSelectedFile(new File(m_sFileName)); } int rval = fc.showSaveDialog(g_panel); if (rval == JFileChooser.APPROVE_OPTION) { // System.out.println("Saving to file \""+ // f.getAbsoluteFile().toString()+"\""); String fileName = fc.getSelectedFile().toString(); if (fileName.lastIndexOf('/') > 0) { m_sDir = fileName.substring(0, fileName.lastIndexOf('/')); } if (!fileName.endsWith(FILE_EXT)) fileName = fileName.concat(FILE_EXT); saveFile(fileName); return true; } return false; } // saveAs protected void saveFile(String fileName) { try { FileWriter outfile = new FileWriter(fileName); outfile.write(m_doc.toXML()); outfile.close(); m_sFileName = fileName; } catch (IOException e) { e.printStackTrace(); } } // saveFile } // class ActionSave class ActionPrint extends ActionSave { /** * for serialisation */ private static final long serialVersionUID = -20389001859354L; boolean m_bIsPrinting = false; public ActionPrint() { super("Print", "Print Graph", "print", KeyEvent.VK_P); } // c'tor @Override public void actionPerformed(ActionEvent ae) { PrinterJob printJob = PrinterJob.getPrinterJob(); printJob.setPrintable(g_panel); if (printJob.printDialog()) try { m_bIsPrinting = true; printJob.print(); m_bIsPrinting = false; } catch (PrinterException pe) { m_bIsPrinting = false; } } // actionPerformed public boolean isPrinting() { return m_bIsPrinting; } } // class ActionPrint class ActionSaveAs extends ActionSave { /** * for serialisation */ private static final long serialVersionUID = -20389110859354L; public ActionSaveAs() { super("Save As", "Save Graph As", "saveas", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { saveAs(); } // actionPerformed } // class ActionSaveAs abstract class MyFileFilter extends FileFilter { @Override public boolean accept(File f) { return f.isDirectory() || f.getName().toLowerCase().endsWith(getExtention()); } abstract public String getExtention(); } class ActionExport extends MyAction { boolean m_bIsExporting = false; /** * for serialisation */ private static final long serialVersionUID = -3027642085935519L; public ActionExport() { super("Export", "Export to graphics file", "export", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_bIsExporting = true; // JFileChooser fc = new JFileChooser(m_sDir); // fc.addChoosableFileFilter(new MyFileFilter() { // public String getExtention() { // return ".bmp"; // } // // public String getDescription() { // return "Bitmap files (*.bmp)"; // } // }); // fc.addChoosableFileFilter(new MyFileFilter() { // public String getExtention() { // return ".jpg"; // } // // public String getDescription() { // return "JPEG bitmap files (*.jpg)"; // } // }); // fc.addChoosableFileFilter(new MyFileFilter() { // public String getExtention() { // return ".png"; // } // // public String getDescription() { // return "PNG bitmap files (*.png)"; // } // }); // fc.setDialogTitle("Export Image As"); // int rval = fc.showSaveDialog(g_panel); // if (rval == JFileChooser.APPROVE_OPTION) { // String fileName = fc.getSelectedFile().toString(); try { File file = Utils.getSaveFile("Export image (type determined by extention)", new File(m_sDir), "Image files", "png", "bmp", "jpg", "svg", "pdf"); if (file != null) { String fileName = file.getAbsolutePath(); if (fileName.lastIndexOf('/') > 0) { m_sDir = fileName.substring(0, fileName.lastIndexOf('/')); } if (fileName != null && !fileName.equals("")) { // if (!fileName.toLowerCase().endsWith(".png") // && fileName.toLowerCase().endsWith(".jpg") // && fileName.toLowerCase().endsWith(".bmp") // && fileName.toLowerCase().endsWith(".svg")) { // fileName += ((MyFileFilter) fc.getFileFilter()) // .getExtention(); // } if (fileName.toLowerCase().endsWith(".pdf")) { JOptionPane.showMessageDialog(null, "Not implemented yet"); // com.itextpdf.text.Document doc = new com.itextpdf.text.Document(); // PdfWriter writer = PdfWriter.getInstance(doc, new FileOutputStream(fileName)); // doc.setPageSize(new com.itextpdf.text.Rectangle(g_panel.getWidth(), g_panel.getHeight())); // doc.open(); // PdfContentByte cb = writer.getDirectContent(); // Graphics2D g2d = new PdfGraphics2D(cb, g_panel.getWidth(), g_panel.getHeight()); // // g_panel.paint(g2d); // // g2d.dispose(); // doc.close(); m_bIsExporting = false; repaint(); return; } else if (fileName.toLowerCase().endsWith(".png") || fileName.toLowerCase().endsWith(".jpg") || fileName.toLowerCase().endsWith(".bmp")) { BufferedImage bi; Graphics g; bi = new BufferedImage(g_panel.getWidth(), g_panel .getHeight(), BufferedImage.TYPE_INT_RGB); g = bi.getGraphics(); g.setPaintMode(); g.setColor(getBackground()); g.fillRect(0, 0, g_panel.getWidth(), g_panel .getHeight()); g_panel.printAll(g); try { if (fileName.toLowerCase().endsWith(".png")) { ImageIO.write(bi, "png", new File(fileName)); } else if (fileName.toLowerCase().endsWith(".jpg")) { ImageIO.write(bi, "jpg", new File(fileName)); } else if (fileName.toLowerCase().endsWith(".bmp")) { ImageIO.write(bi, "bmp", new File(fileName)); } } catch (Exception e) { JOptionPane.showMessageDialog(null, fileName + " was not written properly: " + e.getMessage()); e.printStackTrace(); } m_bIsExporting = false; repaint(); return; } else if (fileName.toLowerCase().endsWith(".svg")) { writeSVG(fileName); m_bIsExporting = false; repaint(); return; } JOptionPane.showMessageDialog(null, "Extention of file " + fileName + " not recognized as png,bmp,jpg or svg file"); } } } catch (Exception e) { JOptionPane.showMessageDialog(null, "Something went wrong while exporting image: " + e.getMessage()); } m_bIsExporting = false; repaint(); } private void writeSVG(String fileName) throws FileNotFoundException { PrintStream out = new PrintStream(fileName); out.println("<?xml version='1.0'?>\n" + "<!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN'\n" + " 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd'>\n" + "<svg xmlns='http://www.w3.org/2000/svg' version='1.1'\n" + " width='" + getWidth() + "' height='" + getHeight() + "' viewBox='0 0 " + getWidth() + " " + getHeight() + "'>\n" + "<rect fill='#fff' width='" + getWidth() + "' height='" + getHeight() + "'/>"); for (Shape shape : m_doc.m_objects) { shape.toSVG(out); } out.println("</svg>"); } public boolean isExporting() { return m_bIsExporting; } } // class ActionExport class ActionQuit extends ActionSave { /** * for serialisation */ private static final long serialVersionUID = -2038911085935515L; public ActionQuit() { super("Exit", "Exit Program", "exit", KeyEvent.VK_F4); } // c'tor @Override public void actionPerformed(ActionEvent ae) { if (!m_doc.m_bIsSaved) { int result = JOptionPane.showConfirmDialog(null, "Drawing changed. Do you want to save it?", "Save before closing?", JOptionPane.YES_NO_CANCEL_OPTION); if (result == JOptionPane.CANCEL_OPTION) { return; } if (result == JOptionPane.YES_OPTION) { if (!saveAs()) { return; } } } System.exit(0); } } // class ActionQuit class ActionNew extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -2038911085935515L; public ActionNew() { super("New", "New Network", "new", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_sFileName = ""; m_doc = new Document(); m_Selection.setDocument(m_doc); updateStatus(); m_doc.clearUndoStack(); m_Selection.clear(); m_drawShape = null; } } // class ActionNew class ActionLoad extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -2038911085935515L; public ActionLoad() { super("Load", "Load Graph", "open", KeyEvent.VK_O); } // c'tor @Override public void actionPerformed(ActionEvent ae) { JFileChooser fc = new JFileChooser(m_sDir); fc.addChoosableFileFilter(ef1); fc.setDialogTitle("Load Graph"); int rval = fc.showOpenDialog(g_panel); if (rval == JFileChooser.APPROVE_OPTION) { String fileName = fc.getSelectedFile().toString(); if (fileName.lastIndexOf('/') > 0) { m_sDir = fileName.substring(0, fileName.lastIndexOf('/')); } g_panel.setCursor(new Cursor(Cursor.WAIT_CURSOR)); m_doc.loadFile(fileName); m_sFileName = fileName; g_panel.repaint(); try { Thread.sleep(1000); } catch (Exception e) { } setDrawingFlag(); g_panel.repaint(); g_panel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } } // class ActionLoad class ActionUndo extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -3038910085935519L; public ActionUndo() { super("Undo", "Undo", "undo", KeyEvent.VK_Z); setEnabled(false); } // c'tor @Override public boolean isEnabled() { return m_doc.canUndo(); } @Override public void actionPerformed(ActionEvent ae) { m_doc.undo(); m_Selection.clear(); setDrawingFlag(); updateStatus(); } } // ActionUndo class ActionRedo extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -4038910085935519L; public ActionRedo() { super("Redo", "Redo", "redo", KeyEvent.VK_Y); setEnabled(false); } // c'tor @Override public boolean isEnabled() { return m_doc.canRedo(); } @Override public void actionPerformed(ActionEvent ae) { m_doc.redo(); m_Selection.clear(); setDrawingFlag(); updateStatus(); } } // ActionRedo class ActionSelectAll extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -2038912085935519L; public ActionSelectAll() { super("Select All", "Select All", "selectall", KeyEvent.VK_A); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_Selection.clear(); for (int i = 0; i < m_doc.m_objects.size(); i++) { if (m_doc.m_objects.get(i).m_bNeedsDrawing) { m_Selection.m_Selection.add(new Integer(i)); } } m_Selection.refreshTracker(); updateStatus(); } } // class ActionSelectAll class ActionDeleteNode extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -2038912085935519L; public ActionDeleteNode() { super("Delete Node", "Delete Node", "delnode", KeyEvent.VK_DELETE); setEnabled(false); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.deleteShapes(m_Selection.m_Selection); m_Selection.clear(); updateStatus(); } } // class ActionDeleteNode class ActionCopyNode extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -2038732085935519L; public ActionCopyNode() { super("Copy", "Copy Nodes", "copy", KeyEvent.VK_C); setEnabled(false); } // c'tor public ActionCopyNode(String name, String toolTipText, String icon, int acceleratorKey) { super(name, toolTipText, icon, acceleratorKey); } // c'rot @Override public void actionPerformed(ActionEvent ae) { copy(); } public void copy() { if (m_Selection.hasSelection()) { String xml = "<doc>\n"; for (int i : m_Selection.m_Selection) { xml += m_doc.m_objects.get(i).getXML() + "\n"; } xml += "</doc>\n"; m_clipboard.setText(xml); } } // copy } // class ActionCopyNode class ActionCutNode extends ActionCopyNode { /** * for serialisation */ private static final long serialVersionUID = -2038822085935519L; public ActionCutNode() { super("Cut", "Cut Nodes", "cut", KeyEvent.VK_X); setEnabled(false); } // c'tor @Override public void actionPerformed(ActionEvent ae) { copy(); m_doc.deleteShapes(m_Selection.m_Selection); m_Selection.clear(); updateStatus(); } } // class ActionCutNode class ActionPasteNode extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -2038732085935519L; public ActionPasteNode() { super("Paste", "Paste Nodes", "paste", KeyEvent.VK_V); setEnabled(false); } // c'tor @Override public void actionPerformed(ActionEvent ae) { try { m_doc.pasteShape(m_clipboard.getText()); updateStatus(); } catch (Exception e) { e.printStackTrace(); } } @Override public boolean isEnabled() { return m_clipboard.hasText(); } } // class ActionPasteNode class ActionCollapse extends MyAction { private static final long serialVersionUID = -1; public ActionCollapse() { super("Collapse shapes", "Collapse", "collapse", -1); KeyStroke keyStroke = KeyStroke.getKeyStroke("G"); putValue(Action.ACCELERATOR_KEY, keyStroke); setEnabled(false); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.collapse(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionGroup // class ActionUngroup extends MyAction { // private static final long serialVersionUID = -1; // public ActionUngroup() { // super("Ungroup shapes", "Ungroup", "ungroup", "Ctrl G"); // setEnabled(false); // } // c'tor // public void actionPerformed(ActionEvent ae) { // m_doc.ungroup(m_Selection); // m_Selection.refreshTracker(); // updateStatus(); // } // } // class ActionUngroup class ActionSelect extends MyAction { private static final long serialVersionUID = -1; public ActionSelect() { super("Select", "Select", "select", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_nMode = MODE_SELECT; } } // class ActionSelect class ActionArrow extends MyAction { private static final long serialVersionUID = -1; public ActionArrow() { super("Arrow", "Arrow", "arrow", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_nMode = MODE_ARROW; } } // class ActionArrow class ActionFunction extends MyAction { private static final long serialVersionUID = -1; public ActionFunction() { super("Function", "Function", "function", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_nMode = MODE_FUNCTION; } } // class ActionFunction class ActionFillColor extends MyAction { private static final long serialVersionUID = -1; public ActionFillColor() { super("Fill color", "Fill color", "fillcolor", -1); setEnabled(false); } // c'tor @Override public void actionPerformed(ActionEvent ae) { Shape shape = m_doc.m_objects.get(m_Selection .getSingleSelection()); Color color = JColorChooser.showDialog(g_panel, "Select Fill color", shape.getFillColor()); if (color != null) { m_doc.setFillColor(color, m_Selection); g_panel.repaint(); } } } // class ActionFillColor class ActionPenColor extends MyAction { private static final long serialVersionUID = -1; public ActionPenColor() { super("Pen color", "Pen color", "pencolor", -1); setEnabled(false); } // c'tor @Override public void actionPerformed(ActionEvent ae) { Shape shape = m_doc.m_objects.get(m_Selection .getSingleSelection()); Color color = JColorChooser.showDialog(g_panel, "Select Fill color", shape.getFillColor()); if (color != null) { m_doc.setPenColor(color, m_Selection); g_panel.repaint(); } } } // class ActionPenColor class ActionToFront extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -1; public ActionToFront() { super("Bring to front", "To front", "tofront", KeyEvent.VK_PLUS); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.toFront(m_Selection); g_panel.repaint(); } } // class ActionToFront class ActionForward extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -1; public ActionForward() { super("Bring forward", "Forward", "forward", -1); KeyStroke keyStroke = KeyStroke.getKeyStroke("plus"); putValue(Action.ACCELERATOR_KEY, keyStroke); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.forward(m_Selection); g_panel.repaint(); } } // class ActionForward class ActionToBack extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -1; public ActionToBack() { super("Bring to back", "To back", "toback", KeyEvent.VK_MINUS); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.toBack(m_Selection); g_panel.repaint(); } } // class ActionToBack class ActionBackward extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -1; public ActionBackward() { super("Bring backward", "Backward", "backward", -1); KeyStroke keyStroke = KeyStroke.getKeyStroke("minus"); putValue(Action.ACCELERATOR_KEY, keyStroke); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.backward(m_Selection); g_panel.repaint(); } } // class ActionBackward class ActionAlignLeft extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -3138642085935519L; public ActionAlignLeft() { super("Align Left", "Align Left", "alignleft", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.alignLeft(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionAlignLeft class ActionAlignRight extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -4238642085935519L; public ActionAlignRight() { super("Align Right", "Align Right", "alignright", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.alignRight(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionAlignRight class ActionAlignTop extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -5338642085935519L; public ActionAlignTop() { super("Align Top", "Align Top", "aligntop", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.alignTop(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionAlignTop class ActionAlignBottom extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -6438642085935519L; public ActionAlignBottom() { super("Align Bottom", "Align Bottom", "alignbottom", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.alignBottom(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionAlignBottom class ActionCenterHorizontal extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -7538642085935519L; public ActionCenterHorizontal() { super("Center Horizontal", "Center Horizontal", "centerhorizontal", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.centreHorizontal(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionCenterHorizontal class ActionCenterVertical extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -8638642085935519L; public ActionCenterVertical() { super("Center Vertical", "Center Vertical", "centervertical", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.centreVertical(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionCenterVertical class ActionSpaceHorizontal extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -9738642085935519L; public ActionSpaceHorizontal() { super("Space Horizontal", "Space Horizontal", "spacehorizontal", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.spaceHorizontal(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionSpaceHorizontal class ActionSpaceVertical extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -838642085935519L; public ActionSpaceVertical() { super("Space Vertical", "Space Vertical", "spacevertical", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_doc.spaceVertical(m_Selection); m_Selection.refreshTracker(); updateStatus(); } } // class ActionSpaceVertical class ActionAbout extends MyAction { /** * for serialisation */ private static final long serialVersionUID = -20389110859353L; public ActionAbout() { super("About", "Help about", "about", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { JOptionPane .showMessageDialog( null, "GenerationD Draw Tool\nRemco Bouckaert\nrrb@xm.co.nz\n2010", "About Message", JOptionPane.PLAIN_MESSAGE); } } // class ActionAbout class ActionRelax extends MyAction { private static final long serialVersionUID = -1; public ActionRelax() { super("Relax", "Relax positions", "about", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_bRelax = !m_bRelax; m_viewRelax.setState(m_bRelax); g_panel.repaint(); } } // class ActionRelax class ActionViewLoggers extends MyAction { private static final long serialVersionUID = -1; public ActionViewLoggers() { super("View loggers", "View loggers", "about", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_bViewLoggers = !m_bViewLoggers; setDrawingFlag(); g_panel.repaint(); } } // class ActionViewLoggers class ActionViewOperators extends MyAction { private static final long serialVersionUID = -1; public ActionViewOperators() { super("View Operators", "View Operators", "viewoperators", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_bViewOperators = !m_bViewOperators; setDrawingFlag(); g_panel.repaint(); } } // class ActionViewOperators class ActionViewSequences extends MyAction { private static final long serialVersionUID = -1; public ActionViewSequences() { super("View Sequences", "View Sequences", "viewsequences", -1); } // c'tor @Override public void actionPerformed(ActionEvent ae) { m_bViewSequences = !m_bViewSequences; setDrawingFlag(); g_panel.repaint(); } } // class ActionViewSequences class ActionLayout extends MyAction { private static final long serialVersionUID = -1; public ActionLayout() { super("Layout", "Layout graph", "layout", -1); } // c'tor @Override public void actionPerformed(ActionEvent e) { g_panel.setCursor(new Cursor(Cursor.WAIT_CURSOR)); m_doc.layout(); m_doc.adjustArrows(); g_panel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); repaint(); g_panel.repaint(); } } // class ActionViewSequences void updateStatus() { a_undo.setEnabled(m_doc.canUndo()); a_redo.setEnabled(m_doc.canRedo()); int selectionSize = m_Selection.m_Selection.size(); boolean hasSelection = (selectionSize > 0); boolean hasGroupSelection = (selectionSize > 1); a_delnode.setEnabled(hasSelection); a_copynode.setEnabled(hasSelection); a_cutnode.setEnabled(hasSelection); a_pastenode.setEnabled(m_clipboard.hasText()); a_fillcolor.setEnabled(hasSelection); a_pencolor.setEnabled(hasSelection); a_forward.setEnabled(hasSelection); a_backward.setEnabled(hasSelection); a_tofront.setEnabled(hasSelection); a_toback.setEnabled(hasSelection); a_group.setEnabled(hasSelection); a_alignbottom.setEnabled(hasGroupSelection); a_aligntop.setEnabled(hasGroupSelection); a_alignleft.setEnabled(hasGroupSelection); a_alignright.setEnabled(hasGroupSelection); a_spacehorizontal.setEnabled(hasGroupSelection); a_spacevertical.setEnabled(hasGroupSelection); a_centerhorizontal.setEnabled(hasGroupSelection); a_centervertical.setEnabled(hasGroupSelection); g_panel.repaint(); } public JToolBar m_jTbTools; public JToolBar m_jTbTools2; public void init() { m_Selection.setDocument(m_doc); int size = UIManager.getFont("Label.font").getSize(); setSize(2048 * size / 13, 2048 * size / 13); g_panel = new DrawPanel(); m_jScrollPane = new JScrollPane(g_panel); makeToolbar(); makeMenuBar(); addComponentListener(this); this.setLayout(new BorderLayout()); this.add(m_jScrollPane, BorderLayout.CENTER); g_panel.setPreferredSize(getSize()); } void makeToolbar() { m_jTbTools = new JToolBar(); m_jTbTools.setFloatable(false); // m_jTbTools.setLayout(new GridBagLayout()); m_jTbTools.add(a_new); m_jTbTools.add(a_save); m_jTbTools.add(a_load); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_cutnode); m_jTbTools.add(a_copynode); m_jTbTools.add(a_pastenode); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_undo); m_jTbTools.add(a_redo); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_select); m_jTbTools.add(a_function); // m_jTbTools.add(a_rect); // m_jTbTools.add(a_rrect); // m_jTbTools.add(a_ellipse); // m_jTbTools.add(a_line); // m_jTbTools.add(a_poly); m_jTbTools.add(a_arrow); m_jTbTools.addSeparator(new Dimension(2, 2)); m_jTbTools.add(a_alignleft); m_jTbTools.add(a_alignright); m_jTbTools.add(a_aligntop); m_jTbTools.add(a_alignbottom); m_jTbTools.add(a_centerhorizontal); m_jTbTools.add(a_centervertical); m_jTbTools.add(a_spacehorizontal); m_jTbTools.add(a_spacevertical); setLayout(new BorderLayout()); // add("North", m_jTbTools); // add("Center", g_panel); m_jTbTools2 = new JToolBar(); m_jTbTools2.setFloatable(false); m_jTbTools2.add(a_relax); m_jTbTools2.add(a_viewLoggers); m_jTbTools2.add(a_viewOperators); m_jTbTools2.add(a_viewSequences); m_jTbTools2.add(a_layout); } // init boolean needsDrawing(BEASTInterface beastObject) { if (beastObject == null) { return true; } if (!m_bViewOperators && beastObject instanceof beast.core.Operator) { return false; } if (!m_bViewLoggers && beastObject instanceof beast.core.Logger) { return false; } if (!m_bViewSequences && beastObject instanceof Sequence) { return false; } if (!m_bViewState && beastObject instanceof beast.core.State) { return false; } return true; } public void setDrawingFlag() { for (int i = 0; i < m_doc.m_objects.size(); i++) { Shape shape = m_doc.m_objects.get(i); shape.m_bNeedsDrawing = false; if (shape.m_bNeedsDrawing) { shape.m_bNeedsDrawing = true; } if (shape instanceof BEASTObjectShape) { BEASTInterface beastObject = ((BEASTObjectShape) shape).m_beastObject; if (needsDrawing(beastObject)) { shape.m_bNeedsDrawing = true; } } else if (shape instanceof InputShape) { BEASTObjectShape beastObjectShape = ((InputShape) shape).m_beastObjectShape; if (beastObjectShape != null) { if (needsDrawing(beastObjectShape.m_beastObject)) { shape.m_bNeedsDrawing = true; } } else { shape.m_bNeedsDrawing = true; } } else if (shape instanceof Arrow) { Shape tail = ((Arrow) shape).m_tailShape; boolean needsDrawing = true; if (tail instanceof BEASTObjectShape) { needsDrawing = needsDrawing(((BEASTObjectShape) tail).m_beastObject); } if (needsDrawing) { Shape head = ((Arrow) shape).m_headShape; if (head instanceof InputShape) { BEASTObjectShape beastObjectShape = ((InputShape) head).m_beastObjectShape; if (beastObjectShape != null) { needsDrawing = needsDrawing(beastObjectShape.m_beastObject); } } if (needsDrawing) { shape.m_bNeedsDrawing = true; } } } else { shape.m_bNeedsDrawing = true; } } } class DrawPanel extends JPanel implements Printable { /** * for serialisation */ static final long serialVersionUID = 1L; public DrawPanel() { setBackground(Color.white); addMouseMotionListener(new GBDrawMouseMotionListener()); addMouseListener(new GBDrawMouseEventListener()); } @Override public void paintComponent(Graphics gr) { Graphics2D g = (Graphics2D) gr; RenderingHints rh = new RenderingHints( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); rh.put(RenderingHints.KEY_RENDERING, RenderingHints.VALUE_RENDER_SPEED); g.setRenderingHints(rh); g.setBackground(Color.WHITE); Rectangle r = g.getClipBounds(); g.clearRect(r.x, r.y, r.width, r.height); m_doc.adjustInputs(); for (int i = 0; i < m_doc.m_objects.size(); i++) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing) { shape.draw(g, this); } } if (!a_export.isExporting() && !a_print.isPrinting()) { if (m_drawShape != null) { m_drawShape.draw(g, this); } if (m_Selection.m_tracker != null) { g.setColor(Color.BLACK); for (int i = 0; i < m_Selection.m_tracker.size(); i++) { TrackPoint p = m_Selection.m_tracker .get(i); g.fillRect(p.m_nX - 4, p.m_nY - 4, 8, 8); } } if (m_selectRect != null) { g.setXORMode(Color.green); g.draw(m_selectRect); } } if (m_bRelax) { m_doc.relax(true); repaint(); } m_jScrollPane.revalidate(); } // paintComponent /** * implementation of Printable, used for printing * * @see Printable */ @Override public int print(Graphics g, PageFormat pageFormat, int pageIndex) { if (pageIndex > 0) { return (NO_SUCH_PAGE); } else { Graphics2D g2d = (Graphics2D) g; g2d.translate(pageFormat.getImageableX(), pageFormat .getImageableY()); // Turn off double buffering paint(g2d); // Turn double buffering back on return (PAGE_EXISTS); } } // print /** * position clicked on */ int m_nPosX = 0, m_nPosY = 0, m_nPoint; boolean m_bIsMoving = false; class GBDrawMouseMotionListener extends MouseMotionAdapter { @Override public void mouseDragged(MouseEvent me) { switch (m_nMode) { case MODE_SELECT: if (m_bIsMoving || m_Selection.isSingleSelection() && m_Selection.intersects(me.getX(), me.getY())) { m_bIsMoving = true; Shape shape = m_doc.m_objects.get(m_Selection .getSingleSelection()); if (getCursor().getType() == Cursor.DEFAULT_CURSOR) { // simple move operation m_doc.moveShape(m_nPosX, m_nPosY, me.getX(), me .getY(), m_Selection.getSingleSelection()); m_Selection.m_tracker = shape.getTracker(); } else { // resize/move point operation m_doc.movePoint(m_nPoint, m_nPosX, m_nPosY, me .getX(), me.getY(), m_Selection .getSingleSelection()); m_Selection.m_tracker = shape.getTracker(); } updateStatus(); break; } else { if (m_selectRect == null) { if (m_Selection.intersects(me.getX(), me.getY())) { // move selection m_nMode = MODE_MOVE; // m_doc.addUndoGroupAction(m_Selection); m_nPosX = me.getX(); m_nPosY = me.getY(); } else { // start new Rectangle m_selectRect = new Rectangle(me.getX(), me .getY(), 1, 1); } } else { m_selectRect.width = me.getX() - m_selectRect.x; m_selectRect.height = me.getY() - m_selectRect.y; } g_panel.repaint(); } break; case MODE_MOVE: int dX = me.getX() - m_nPosX; int dY = me.getY() - m_nPosY; m_doc.moveShapes(dX, dY, m_Selection.m_Selection); m_Selection.offset(dX, dY); m_doc.adjustArrows(); m_nPosX = me.getX(); m_nPosY = me.getY(); repaint(); break; case MODE_FUNCTION: beast.app.draw.BEASTObjectShape function = (beast.app.draw.BEASTObjectShape) m_drawShape; if (m_drawShape == null) { function = new beast.app.draw.BEASTObjectShape(); function.m_x = me.getX(); function.m_y = me.getY(); function.m_w = 1; function.m_h = 1; m_drawShape = function; } else { function.m_w = me.getX() - function.m_x; function.m_h = me.getY() - function.m_y; } m_Selection.m_tracker = function.getTracker(); g_panel.repaint(); break; case MODE_ARROW: Arrow arrow = (Arrow) m_drawShape; if (m_drawShape == null) { int selectionIndex = -1; for (int i = 0; selectionIndex < 0 && i < m_doc.m_objects.size(); i++) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing && shape.intersects(me.getX(), me.getY())) { m_nPosX = shape.offsetX(me.getX()); m_nPosY = shape.offsetY(me.getY()); selectionIndex = i; } } if (selectionIndex < 0) { return; } Shape shape = m_doc.m_objects.get(selectionIndex); if (shape instanceof Arrow) { return; } if (shape instanceof InputShape) { shape = ((InputShape) shape).m_beastObjectShape; } arrow = new Arrow((BEASTObjectShape) shape, me.getX(), me .getY()); arrow.m_w = 1; arrow.m_h = 1; m_drawShape = arrow; } else { arrow.setHead(me.getX() - arrow.m_x, me.getY() - arrow.m_y); } // m_Selection.m_tracker = arrow.getTracker(); g_panel.repaint(); break; } } // mouseDragged @Override public void mouseMoved(MouseEvent me) { if (m_Selection.m_tracker != null && m_Selection.isSingleSelection()) { if (getCursor().getType() != Cursor.DEFAULT_CURSOR) { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } for (int i = 0; i < m_Selection.m_tracker.size(); i++) { TrackPoint p = m_Selection.m_tracker .get(i); if (me.getX() > p.m_nX - 4 && me.getX() < p.m_nX + 4 && me.getY() > p.m_nY - 4 && me.getY() < p.m_nY + 4) { m_nPoint = i; m_nPosX = me.getX() - p.m_nX; m_nPosY = me.getY() - p.m_nY; setCursor(new Cursor(p.m_nCursor)); return; } } Shape shape = m_doc.m_objects.get(m_Selection .getSingleSelection()); m_nPosX = me.getX() - shape.m_x; m_nPosY = me.getY() - shape.m_y; } else { if (getCursor().getType() != Cursor.DEFAULT_CURSOR) { setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); } } // set up tool tip text for (int i = m_doc.m_objects.size() - 1; i >= 0; i--) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing && shape.intersects(me.getX(), me.getY())) { if (shape instanceof BEASTObjectShape) { BEASTObjectShape beastObject = (BEASTObjectShape) shape; try { String toolTip = "<html>"; for (InputShape input : beastObject.m_inputs) { toolTip += input.getLongLabel() + "<br>"; } toolTip += "</html>"; setToolTipText(toolTip); } catch (IllegalArgumentException e) { // TODO Auto-generated catch block e.printStackTrace(); } } return; } } } // mouseMoved } // class GBDrawMouseMotionListener class GBDrawMouseEventListener extends MouseAdapter { @Override public void mouseClicked(MouseEvent me) { if (me.getClickCount() == 2) { handleDoubleClick(me); return; } if (me.getButton() == MouseEvent.BUTTON3) { handleRightClick(me); return; } // otherwise, assume left click switch (m_nMode) { case MODE_SELECT: int selectionIndex = -1; for (int i = m_doc.m_objects.size() - 1; selectionIndex < 0 && i >= 0; i--) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing && !(shape instanceof Arrow) && shape.intersects(me.getX(), me.getY())) { m_nPosX = shape.offsetX(me.getX()); m_nPosY = shape.offsetY(me.getY()); selectionIndex = i; } } if (selectionIndex < 0) { for (int i = m_doc.m_objects.size() - 1; selectionIndex < 0 && i >= 0; i--) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing && shape.intersects(me.getX(), me.getY())) { m_nPosX = shape.offsetX(me.getX()); m_nPosY = shape.offsetY(me.getY()); selectionIndex = i; } } if (selectionIndex < 0) { return; } } if ((me.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) { m_Selection.toggleSelection(selectionIndex); } else if ((me.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) != 0) { m_Selection.addToSelection(selectionIndex); } else { m_Selection.clear(); m_Selection.setSingleSelection(selectionIndex); } updateStatus(); break; } } // mouseClicked void handleDoubleClick(MouseEvent me) { // find the shape intersecting the mouse pointer try { for (int i = m_doc.m_objects.size() - 1; i >= 0; i--) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing && shape.intersects(me.getX(), me.getY())) { if (shape instanceof InputShape) { // resolve the associated input InputShape ellipse = (InputShape) shape; String input = ellipse.getInputName(); BEASTInterface beastObject = ellipse.getBEASTObject(); if (beastObject.isPrimitive(input)) { String valueString = ""; if (beastObject.getInputValue(input) != null) { valueString = beastObject.getInputValue(input) .toString(); } valueString = JOptionPane.showInputDialog(input + ":", valueString); if (valueString != null) { m_doc.setInputValue(ellipse.getPluginShape(), input, valueString); // ellipse.setLabel(input + "=" + // beastObject.getInputValue(input).toString()); g_panel.repaint(); } } } return; } } } catch (Exception e) { e.printStackTrace(); } } // handleDoubleClick void handleRightClick(MouseEvent me) { if (!m_bIsEditable) { return; } JPopupMenu popupMenu = new JPopupMenu("Choose a value"); if (!m_Selection.hasSelection()) { int selectionIndex = -1; for (int i = 0; selectionIndex < 0 && i < m_doc.m_objects.size(); i++) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing && shape.intersects(me.getX(), me.getY())) { m_nPosX = shape.offsetX(me.getX()); m_nPosY = shape.offsetY(me.getY()); m_Selection.addToSelection(i); break; } } } JMenuItem addNodeItem = new JMenuItem("Change label"); ActionListener label = new ActionListener() { @Override public void actionPerformed(ActionEvent ae) { Shape shape = m_Selection.getSingleSelectionShape(); String name = (String) JOptionPane.showInputDialog( null, shape.getID(), "New label", JOptionPane.OK_CANCEL_OPTION, null, null, shape .getID()); if (name == null || name.equals("")) { return; } while (name.contains("\\n")) { int i = name.indexOf("\\n"); name = name.substring(0, i - 1) + '\n' + name.substring(i + 2); } m_doc.setID(name, m_Selection.getSingleSelection()); repaint(); } }; addNodeItem.addActionListener(label); addNodeItem.setEnabled(m_Selection.isSingleSelection()); popupMenu.add(addNodeItem); JMenuItem isFilledMenu = new JMenuItem("Fill object"); if (m_Selection.isSingleSelection()) { Shape shape = m_Selection.getSingleSelectionShape(); if (shape.isFilled()) { isFilledMenu = new JMenuItem("Don't fill object"); } } isFilledMenu.addActionListener(ae -> { m_doc.toggleFilled(m_Selection.getSingleSelection()); g_panel.repaint(); }); isFilledMenu.setEnabled(m_Selection.isSingleSelection()); popupMenu.add(isFilledMenu); JMenuItem propertiesItem = new JMenuItem("Properties"); propertiesItem.addActionListener(ae -> { Shape shape = m_Selection.getSingleSelectionShape(); if (shape instanceof BEASTObjectShape) { BEASTInterface beastObject = ((BEASTObjectShape) shape).m_beastObject; List<BEASTInterface> beastObjects = new ArrayList<>(); for (Shape shape2 : m_doc.m_objects) { if (shape2 instanceof BEASTObjectShape) { beastObjects.add(((BEASTObjectShape) shape2).m_beastObject); } } BEASTObjectDialog dlg = new BEASTObjectDialog(beastObject, beastObject.getClass(), beastObjects, null); if (dlg.showDialog()) { // add newly created Plug-ins int newShapes = 0; if (beastObjects.size() < BEASTObjectPanel.g_plugins.size()) { for (BEASTInterface beastObject2 : BEASTObjectPanel.g_plugins.values()) { if (!beastObjects.contains(beastObject2)) { try { newShapes++; Shape shape2 = new BEASTObjectShape(beastObject2, m_doc); shape2.m_x = 10; shape2.m_y = newShapes * 50; shape2.m_w = 80; m_doc.addNewShape(shape2); updateStatus(); } catch (Exception e) { e.printStackTrace(); } } } } // re-attach all arrows m_Selection.clear(); m_doc.recalcArrows(); m_doc.adjustArrows(); } repaint(); } }); propertiesItem.setEnabled(m_Selection.isSingleSelection()); popupMenu.add(propertiesItem); JMenuItem saveAsItem = new JMenuItem("Save as"); saveAsItem.addActionListener(ae -> { Shape shape = m_Selection.getSingleSelectionShape(); BEASTInterface beastObject = ((BEASTObjectShape) shape).m_beastObject; JFileChooser fc = new JFileChooser(m_sDir); fc.addChoosableFileFilter(ef1); fc.setDialogTitle("Save BEASTObject As"); if (!m_sFileName.equals("")) { // can happen on actionQuit fc.setSelectedFile(new File(m_sFileName)); } int rval = fc.showSaveDialog(g_panel); if (rval == JFileChooser.APPROVE_OPTION) { // System.out.println("Saving to file \""+ // f.getAbsoluteFile().toString()+"\""); String fileName = fc.getSelectedFile().toString(); if (fileName.lastIndexOf('/') > 0) { m_sDir = fileName.substring(0, fileName .lastIndexOf('/')); } if (!fileName.endsWith(FILE_EXT)) fileName = fileName.concat(FILE_EXT); try { FileWriter outfile = new FileWriter(fileName); outfile.write(new XMLProducer().modelToXML(beastObject)); outfile.close(); } catch (Exception e) { JOptionPane.showMessageDialog(null, "Something went wrong when writing the file: " + e.getClass().getName() + " " + e.getMessage()); } m_sFileName = fileName; } repaint(); }); saveAsItem.setEnabled(m_Selection.isSingleSelection()); popupMenu.add(saveAsItem); popupMenu.setLocation(me.getX(), me.getY()); popupMenu.show(g_panel, me.getX(), me.getY()); } // handleRightClick @Override public void mouseReleased(MouseEvent me) { if (m_drawShape != null) { m_drawShape.normalize(); } m_bIsMoving = false; boolean added = true; switch (m_nMode) { case MODE_SELECT: if (m_selectRect != null) { if ((me.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) == 0 && (me.getModifiersEx() & InputEvent.SHIFT_DOWN_MASK) == 0) { // fresh selection m_Selection.clear(); } for (int i = 0; i < m_doc.m_objects.size(); i++) { if (m_doc.m_objects.get(i) .intersects(m_selectRect) && m_doc.m_objects.get(i).m_bNeedsDrawing) { if ((me.getModifiersEx() & InputEvent.CTRL_DOWN_MASK) != 0) { m_Selection.toggleSelection(i); } else { m_Selection.addToSelection(i); } } } m_selectRect = null; updateStatus(); } return; case MODE_MOVE: m_nMode = MODE_SELECT; updateStatus(); return; case MODE_FUNCTION: BEASTObjectShape function = (BEASTObjectShape) m_drawShape; if (function == null) { return; } if (function.m_w > 0 && function.m_h > 0) { String functionClassName = (String) JOptionPane .showInputDialog(g_panel, "Select a constant", "select", JOptionPane.PLAIN_MESSAGE, null, m_doc.m_sPlugInNames, null); if (functionClassName != null) { try { function.init(functionClassName, m_doc); m_doc.addNewShape(function); updateStatus(); } catch (Exception e) { // TODO: handle exception e.printStackTrace(); } } else { added = false; } } else { added = false; } break; case MODE_ARROW: Arrow arrow = (Arrow) m_drawShape; if (m_drawShape != null) { int selectionIndex = -1; for (int i = 0; selectionIndex < 0 && i < m_doc.m_objects.size(); i++) { Shape shape = m_doc.m_objects.get(i); if (shape.m_bNeedsDrawing && shape.intersects(me.getX(), me.getY())) { m_nPosX = shape.offsetX(me.getX()); m_nPosY = shape.offsetY(me.getY()); selectionIndex = i; } } if (selectionIndex < 0) { m_drawShape = null; repaint(); return; } Shape target = m_doc.m_objects.get(selectionIndex); if (!(target instanceof InputShape)) { // only connect to inputs of functions m_drawShape = null; repaint(); return; } // check no cycle is introduced InputShape target2 = (InputShape) target; if (m_doc.isAscendant(arrow.m_tailShape.m_beastObject, target2.getBEASTObject())) { JOptionPane .showMessageDialog(null, "Cannot make this connection since this creates a cycle in the model"); return; } try { // try to add connection // this links the input of the target beastObject // to the source beastObject. If types mismatch, // an exception is thrown and no arrow added. arrow.setHead(target2, m_doc.m_objects, m_doc); m_doc.addNewShape(arrow); } catch (Exception e) { JOptionPane.showMessageDialog(null, e.getMessage()); repaint(); } } break; } m_drawShape = null; m_nMode = MODE_SELECT; if (added) { m_Selection.setSingleSelection(m_doc.m_objects.size() - 1); g_panel.repaint(); } else { m_Selection.clear(); g_panel.repaint(); } } // mouseReleased } // GBDrawMouseEventListener } // class DrawPanel public JMenuBar makeMenuBar() { JMenuBar menuBar = new JMenuBar(); JMenu fileMenu = new JMenu("File"); fileMenu.setMnemonic('F'); menuBar.add(fileMenu); fileMenu.add(a_new); fileMenu.add(a_load); fileMenu.add(a_save); fileMenu.add(a_saveas); fileMenu.addSeparator(); fileMenu.add(a_export); fileMenu.add(a_print); fileMenu.addSeparator(); fileMenu.add(a_quit); JMenu editMenu = new JMenu("Edit"); editMenu.setMnemonic('E'); menuBar.add(editMenu); editMenu.add(a_undo); editMenu.add(a_redo); editMenu.addSeparator(); editMenu.add(a_selectall); editMenu.add(a_delnode); editMenu.add(a_cutnode); editMenu.add(a_copynode); editMenu.add(a_pastenode); editMenu.addSeparator(); editMenu.add(a_alignleft); editMenu.add(a_alignright); editMenu.add(a_aligntop); editMenu.add(a_alignbottom); editMenu.add(a_centerhorizontal); editMenu.add(a_centervertical); editMenu.add(a_spacehorizontal); editMenu.add(a_spacevertical); editMenu.addSeparator(); editMenu.add(a_group); // editMenu.add(a_ungroup); editMenu.addSeparator(); JMenu drawMenu = new JMenu("Draw"); drawMenu.setMnemonic('D'); menuBar.add(drawMenu); drawMenu.add(a_select); drawMenu.add(a_function); drawMenu.add(a_arrow); JMenu objectMenu = new JMenu("Object"); objectMenu.setMnemonic('O'); menuBar.add(objectMenu); objectMenu.add(a_fillcolor); objectMenu.add(a_pencolor); objectMenu.addSeparator(); objectMenu.add(a_tofront); objectMenu.add(a_forward); objectMenu.add(a_toback); objectMenu.add(a_backward); JMenu viewMenu = new JMenu("View"); viewMenu.setMnemonic('V'); menuBar.add(viewMenu); final JCheckBoxMenuItem viewOperators = new JCheckBoxMenuItem( "Show Operators", m_bViewOperators); viewOperators.addActionListener(ae -> { m_bViewOperators = viewOperators.getState(); setDrawingFlag(); g_panel.repaint(); }); viewMenu.add(viewOperators); final JCheckBoxMenuItem viewLoggers = new JCheckBoxMenuItem( "Show Loggers", m_bViewLoggers); viewLoggers.addActionListener(ae -> { m_bViewLoggers = viewLoggers.getState(); setDrawingFlag(); g_panel.repaint(); }); viewMenu.add(viewLoggers); final JCheckBoxMenuItem viewSequences = new JCheckBoxMenuItem( "Show Sequences", m_bViewSequences); viewSequences.addActionListener(ae -> { m_bViewSequences = viewSequences.getState(); setDrawingFlag(); g_panel.repaint(); }); viewMenu.add(viewSequences); final JCheckBoxMenuItem viewState = new JCheckBoxMenuItem("Show State", m_bViewState); viewState.addActionListener(ae -> { m_bViewState = viewState.getState(); setDrawingFlag(); g_panel.repaint(); }); viewMenu.add(viewState); m_viewRelax = new JCheckBoxMenuItem("Relax", m_bRelax); m_viewRelax.addActionListener(ae -> { m_bRelax = m_viewRelax.getState(); g_panel.repaint(); }); viewMenu.add(m_viewRelax); JMenuItem layoutMenu = new JMenuItem("Layout Visible Items"); layoutMenu.setMnemonic('L'); layoutMenu.addActionListener(e -> { g_panel.setCursor(new Cursor(Cursor.WAIT_CURSOR)); m_doc.layout(); m_doc.adjustArrows(); repaint(); g_panel.setCursor(new Cursor(Cursor.DEFAULT_CURSOR)); }); viewMenu.add(layoutMenu); viewMenu.addSeparator(); final JCheckBoxMenuItem viewAllInputs = new JCheckBoxMenuItem("Show All Inputs", m_doc.m_bShowALlInputs); viewAllInputs.addActionListener(ae -> { m_doc.m_bShowALlInputs = viewAllInputs.getState(); m_doc.reinit(); setDrawingFlag(); g_panel.repaint(); }); viewMenu.add(viewAllInputs); final JCheckBoxMenuItem sanitseIDs = new JCheckBoxMenuItem("Sanitise IDs", m_doc.m_bSanitiseIDs); sanitseIDs.addActionListener(ae -> { m_doc.m_bSanitiseIDs = sanitseIDs.getState(); setDrawingFlag(); g_panel.repaint(); }); viewMenu.add(sanitseIDs); JMenu helpMenu = new JMenu("Help"); helpMenu.setMnemonic('H'); menuBar.add(helpMenu); helpMenu.add(a_about); return menuBar; } // makeMenuBar public static void main(String args[]) { Randomizer.setSeed(127); try { AddOnManager.loadExternalJars(); } catch (Exception e) { e.printStackTrace(); } JFrame f = new JFrame("Model Builder"); ModelBuilder drawTest = new ModelBuilder(); drawTest.init(); JMenuBar menuBar = drawTest.makeMenuBar(); f.setJMenuBar(menuBar); f.add(drawTest.m_jTbTools, BorderLayout.NORTH); f.add(drawTest, BorderLayout.CENTER); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); java.net.URL tempURL = ClassLoader .getSystemResource(ModelBuilder.ICONPATH + "/GenerationD.png"); try { f.setIconImage(ImageIO.read(tempURL)); } catch (Exception e) { e.printStackTrace(); // ignore } if (args.length > 0) { drawTest.m_doc.loadFile(args[0]); drawTest.setDrawingFlag(); } int size = UIManager.getFont("Label.font").getSize(); f.setSize(800 * size / 13, 600 * size / 13); f.setVisible(true); } // main // implements ComponentListener @Override public void componentHidden(ComponentEvent e) { m_jScrollPane.revalidate(); } // implements ComponentListener @Override public void componentMoved(ComponentEvent e) { m_jScrollPane.revalidate(); } // implements ComponentListener @Override public void componentResized(ComponentEvent e) { m_jScrollPane.revalidate(); } // implements ComponentListener @Override public void componentShown(ComponentEvent e) { m_jScrollPane.revalidate(); } } // class ModelBuilder