/* * SynthDefDiagram.java * JCollider * * Copyright (c) 2004-2010 Hanns Holger Rutz. All rights reserved. * * This software 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 2, june 1991 of the License, or (at your option) any later version. * * This software 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 (gpl.txt) along with this software; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA * * * For further information, please contact Hanns Holger Rutz at * contact@sciss.de , or visit http://www.sciss.de/jcollider * * * JCollider is closely modelled after SuperCollider Language, * often exhibiting a direct translation from Smalltalk to Java. * SCLang is a software originally developed by James McCartney, * which has become an Open Source project. * See http://www.audiosynth.com/ for details. * * * Changelog: * 30-Jun-05 created */ package de.sciss.jcollider.gui; import java.awt.BasicStroke; import java.awt.BorderLayout; import java.awt.Color; import java.awt.Container; import java.awt.Dimension; import java.awt.Font; import java.awt.FontMetrics; import java.awt.Graphics; import java.awt.Graphics2D; import java.awt.Paint; import java.awt.Point; import java.awt.RenderingHints; import java.awt.Shape; import java.awt.Stroke; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.awt.event.MouseEvent; import java.awt.event.MouseListener; import java.awt.event.MouseMotionListener; import java.awt.geom.AffineTransform; import java.awt.geom.Area; import java.awt.geom.GeneralPath; import java.awt.geom.Line2D; import java.awt.geom.Point2D; import java.awt.geom.Rectangle2D; import java.awt.geom.RoundRectangle2D; import java.text.NumberFormat; import java.text.ParseException; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import javax.swing.Box; import javax.swing.JComboBox; import javax.swing.JFrame; import javax.swing.JPanel; import javax.swing.JScrollPane; import de.sciss.jcollider.Constant; import de.sciss.jcollider.Constants; import de.sciss.jcollider.SynthDef; import de.sciss.jcollider.UGen; import de.sciss.jcollider.UGenChannel; import de.sciss.jcollider.UGenInfo; import de.sciss.jcollider.UGenInput; /** * An experimental view of a SynthDef's graph. * This still lacks intelligent code to * bring a form into the wires, but for * small ugens and for debugging it's quite useful. * <p> * Colours are as follows: * <ul> * <li>red : audio rate</li> * <li>blue : control rate</li> * <li>green : demand rate</li> * <li>grey : scalars</li> * </ul> * <p> * The view can be magnified and boxes can * be dragged around. Double clicking on a * UGen will reveal the names of its inlets. * * @version 0.32, 25-Feb-08 * @author Hanns Holger Rutz */ public class SynthDefDiagram extends JFrame { private final Font fntGUI = new Font( "SansSerif", Font.PLAIN, 10 ); private static final String[] ZOOMS = { "800%", "400%", "200%", "150%", "125%", "100%", "75%", "50%", "25%", "12.5%" }; protected static final NumberFormat frmtZoom = NumberFormat.getPercentInstance( Locale.US ); /** * Creates a new frame displaying * the diagram of the provided SynthDef. * The frame is automatically made visible. * * @param def the synth def to illustrate */ public SynthDefDiagram( SynthDef def ) { super( "SynthDef(\"" + def.getName() + "\")" ); final Container cp = getContentPane(); // frame.getRootPane().setBorder( BorderFactory.createEmptyBorder( 8, 8, 8, 8 )); final SynthDefView synthDefView = new SynthDefView( def ); final JScrollPane scroll = new JScrollPane( synthDefView ); final Box box = Box.createHorizontalBox(); final JComboBox ggZoom = new JComboBox(); for( int i = 0; i < ZOOMS.length; i++ ) { ggZoom.addItem( ZOOMS[ i ]); } ggZoom.setSelectedIndex( 5 ); ggZoom.setEditable( true ); ggZoom.setFont( fntGUI ); ggZoom.addActionListener( new ActionListener() { public void actionPerformed( ActionEvent e ) { final String text = ggZoom.getSelectedItem().toString(); Number num = null; try { num = frmtZoom.parse( text ); } catch( ParseException e1 ) { /* ignored */ } if( num != null ) { synthDefView.setZoom( num.doubleValue() ); } else { ggZoom.setSelectedItem( frmtZoom.format( synthDefView.getZoom() )); } } }); box.add( ggZoom ); box.add( Box.createHorizontalGlue() ); cp.setLayout( new BorderLayout() ); cp.add( scroll, BorderLayout.CENTER ); cp.add( box, BorderLayout.SOUTH ); setSize( 320, 320 ); setVisible( true ); toFront(); } protected static String formatConst( float value ) { if( value == Math.round( value )) return String.valueOf( Math.round( value )); return frmtConst.format( new Float( value )); } protected static final Font fntUGen = new Font( "Lucida Grande", Font.PLAIN, 10 ); // XXX bad for non-macos protected static final Font fntToolTip = new Font( "Gill Sans", Font.ITALIC, 12 ); // XXX bad for non-macos private static final NumberFormat frmtConst = NumberFormat.getInstance( Locale.US ); static { frmtConst.setGroupingUsed( false ); frmtConst.setMaximumFractionDigits( 4 ); } private class SynthDefView // extends JComponent extends JPanel implements MouseListener, MouseMotionListener { private static final double HPAD = 12.0; private static final double VPAD = 12.0; private static final int MAX_WIDTH = 640; // linebreak after exceeding this width private final List collUGenViews = new ArrayList(); private final List collSelectedViews = new ArrayList(); private final List collWires = new ArrayList(); private final Map mapUGensToViews = new HashMap(); private boolean recalc = true; private final SynthDef def; private Point dragStartPtScreen = null; private Point2D dragStartPt = null; private Point2D dragCurrentPt = null; private boolean dragStarted = false; private double zoom = 1.0; private Rectangle2D boundingBox = new Rectangle2D.Double(); protected SynthDefView( SynthDef def ) { super(); this.def = def; this.setFont( fntUGen ); // this.setBackground( null ); // this.setOpaque( false ); this.addMouseListener( this ); this.addMouseMotionListener( this ); this.setFocusable( true ); } private void createBoxes( Graphics2D g2, FontMetrics fm, FontMetrics fm2 ) { final List children = def.getUGens(); final List verbaut = new ArrayList(); final List neuVerbaut = new ArrayList(); final List constRects = new ArrayList(); final List ugenRects = new ArrayList(); final double h = fm.getHeight() + VPAD; double y = 0; double x, w, hpadMax, constW, incY; int cons; Rectangle2D rect, rect2; Point2D pt; UGen ugen; UGenView uv, uv2; UGenInput inp; UGenInfo ui; Wire wire; String name; collUGenViews.clear(); mapUGensToViews.clear(); collWires.clear(); // ugenRects.add( new Rectangle( 0, -10, 65536, 10 )); // simulates top window border do { x = 0; childLp: for( int i = 0; i < children.size(); i++ ) { ugen = (UGen) children.get( i ); ui = UGenInfo.infos == null ? null : (UGenInfo) UGenInfo.infos.get( ugen.getName() ); hpadMax = HPAD; for( int j = 0; j < ugen.getNumInputs(); j++ ) { inp = ugen.getInput( j ); if( inp instanceof UGenChannel ) { if( !verbaut.contains( ((UGenChannel) inp).getUGen() )) continue childLp; } else if( inp instanceof Constant ) { constW = 2 + fm.getStringBounds( formatConst( ((Constant) inp).getValue() ), g2 ).getWidth(); hpadMax = Math.max( hpadMax, constW ); } } // all right, all inputs ready children.remove( i-- ); name = ui == null ? ugen.getName() : ui.getDisplayName( ugen ); rect = fm.getStringBounds( name, g2 ); cons = Math.max( ugen.getNumInputs(), ugen.getNumOutputs() ); w = Math.max( cons * (2 + hpadMax), rect.getWidth() + HPAD ); uv = new UGenView( ugen, ui, name, new Rectangle2D.Double( x, y, w, h ), fm, fm2 ); collUGenViews.add( uv ); mapUGensToViews.put( ugen, uv ); neuVerbaut.add( ugen ); constRects.addAll( uv.getConstBounds() ); for( int j = 0; j < ugen.getNumInputs(); j++ ) { inp = ugen.getInput( j ); if( inp instanceof UGenChannel ) { uv2 = (UGenView) mapUGensToViews.get( ((UGenChannel) inp).getUGen() ); wire = new Wire( uv2, ((UGenChannel) inp).getChannel(), uv, j ); collWires.add( wire ); } } x += w + HPAD; if( x > MAX_WIDTH ) break childLp; } // shift line downwards if there are vertical overlappings incY = 0.0; for( int i = 0; i < constRects.size(); i++ ) { rect = (Rectangle2D) constRects.get( i ); for( int j = 0; j < ugenRects.size(); j++ ) { rect2 = (Rectangle2D) ugenRects.get( j ); //System.err.println( "const "+rect.getX()+","+rect.getY()+","+rect.getWidth()+","+rect.getHeight()+"; ugen "+ // rect2.getX()+","+rect2.getY()+","+rect2.getWidth()+","+rect2.getHeight() ); if( rect.intersects( rect2 )) { //System.err.println( "intersects. dy = "+ (rect.getY() - (rect2.getY() + rect2.getHeight()) + 4) ); incY = Math.max( incY, (rect2.getY() + rect2.getHeight()) - rect.getY() + 6 ); } } } ugenRects.clear(); constRects.clear(); for( int i = 0; i < neuVerbaut.size(); i++ ) { uv = (UGenView) mapUGensToViews.get( neuVerbaut.get( i )); if( incY > 0.0 ) { pt = uv.getLocation(); uv.setLocation( new Point2D.Double( pt.getX(), pt.getY() + incY )); } ugenRects.add( uv.getContainer() ); } verbaut.addAll( neuVerbaut ); for( int i = 0; i < neuVerbaut.size(); i++ ) { uv = (UGenView) mapUGensToViews.get( neuVerbaut.get( i )); ugenRects.add( uv.getContainer() ); } neuVerbaut.clear(); y += h + VPAD + incY; } while( !children.isEmpty() ); recalcBoundingBox(); recalc = false; } protected double getZoom() { return zoom; } protected void setZoom( double newZoom ) { zoom = newZoom; // final Dimension oldSize = getPreferredSize(); final Dimension newSize = new Dimension( (int) (boundingBox.getMaxX() * zoom + 4), (int) (boundingBox.getMaxY() * zoom + 4) ); // if( !oldSize.equals( newSize )) { setPreferredSize( newSize ); revalidate(); // } // repaint(); } private void recalcBoundingBox() { if( collUGenViews.isEmpty() ) return; UGenView uv = (UGenView) collUGenViews.get( 0 ); boundingBox = uv.getBoundingBox(); for( int i = 1; i < collUGenViews.size(); i++ ) { uv = (UGenView) collUGenViews.get( i ); Rectangle2D.union( boundingBox, uv.getBoundingBox(), boundingBox ); } final Dimension oldSize = getPreferredSize(); final Dimension newSize = new Dimension( (int) (boundingBox.getMaxX() * zoom + 4), (int) (boundingBox.getMaxY() * zoom + 4) ); //System.err.println( "bounding box: "+box.getX()+","+box.getY()+","+box.getWidth()+","+box.getHeight() ); if( (boundingBox.getX() < 4) || (boundingBox.getY() < 4) ) { final int dx = Math.max( 0, (int) (5 - boundingBox.getX()) ); final int dy = Math.max( 0, (int) (5 - boundingBox.getY()) ); Point2D pt; for( int i = 0; i < collUGenViews.size(); i++ ) { uv = (UGenView) collUGenViews.get( i ); pt = uv.getLocation(); uv.setLocation( new Point2D.Double( pt.getX() + dx, pt.getY() + dy )); } // final Point viewPt = viewport.getViewPosition(); // viewport.setViewPosition( new Point( viewPt.x - dx, viewPt.y - dy )); } if( !oldSize.equals( newSize )) { //System.err.println( "setting new size "+newSize.getWidth()+","+newSize.getHeight() ); setPreferredSize( newSize ); revalidate(); } } public void paintComponent( Graphics g ) { super.paintComponent( g ); final Graphics2D g2 = (Graphics2D) g; final FontMetrics fm = g2.getFontMetrics(); final FontMetrics fm2 = g2.getFontMetrics( fntToolTip ); final AffineTransform atOrig = g2.getTransform(); g2.setRenderingHint( RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON ); g2.setRenderingHint( RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON ); g2.scale( zoom, zoom ); if( recalc ) { createBoxes( g2, fm, fm2 ); } UGenView uv; Wire wire; for( int i = 0; i < collUGenViews.size(); i++ ) { uv = (UGenView) collUGenViews.get( i ); uv.paint( g2, fm, false ); } for( int i = 0; i < collWires.size(); i++ ) { wire = (Wire) collWires.get( i ); wire.paint( g2 ); } g2.setFont( fntToolTip ); for( int i = 0; i < collUGenViews.size(); i++ ) { uv = (UGenView) collUGenViews.get( i ); if( uv.isShowingToolTips() ) uv.paintToolTips( g2, fm2 ); } g2.setFont( fntUGen ); if( dragStarted ) { g2.translate( dragCurrentPt.getX() - dragStartPt.getX(), dragCurrentPt.getY() - dragStartPt.getY() ); for( int i = 0; i < collSelectedViews.size(); i++ ) { uv = (UGenView) collSelectedViews.get( i ); uv.paint( g2, fm, true ); } } g2.setTransform( atOrig ); } private Point2D getVirtualPoint( Point screenPt ) { return new Point2D.Double( screenPt.x / zoom, screenPt.y / zoom ); } public void mousePressed( MouseEvent e ) { requestFocus(); final Point2D mousePt = getVirtualPoint( e.getPoint() ); UGenView hitView = null; boolean repaint = false; UGenView uv; for( int i = 0; i < collUGenViews.size(); i++ ) { uv = (UGenView) collUGenViews.get( i ); if( uv.contains( mousePt )) { hitView = uv; break; } } if( (!e.isShiftDown() && !collSelectedViews.isEmpty()) && ((hitView == null) || !hitView.isSelected()) ) { // deselect all for( int i = 0; i < collSelectedViews.size(); i++ ) { uv = (UGenView) collSelectedViews.get( i ); uv.setSelected( false ); } collSelectedViews.clear(); repaint = true; } if( hitView != null ) { if( e.getClickCount() == 2 ) { hitView.showToolTips( !hitView.isShowingToolTips() ); repaint = true; } if( e.isShiftDown() ) { if( hitView.isSelected() ) { hitView.setSelected( false ); collSelectedViews.remove( hitView ); } else { hitView.setSelected( true ); collSelectedViews.add( hitView ); } repaint = true; } else { if( !hitView.isSelected() ) { hitView.setSelected( true ); collSelectedViews.add( hitView ); repaint = true; } } } else { if( e.getClickCount() == 2 ) { for( int i = 0; i < collUGenViews.size(); i++ ) { uv = (UGenView) collUGenViews.get( i ); uv.showToolTips( false ); } repaint = true; } } if( !collSelectedViews.isEmpty() ) { dragStartPtScreen = e.getPoint(); dragStartPt = mousePt; dragStarted = false; } if( repaint ) repaint(); } public void mouseReleased( MouseEvent e ) { UGenView uv; Point2D pt; final double dx, dy; if( dragStarted ) { dragCurrentPt = getVirtualPoint( e.getPoint() ); dx = dragCurrentPt.getX() - dragStartPt.getX(); dy = dragCurrentPt.getY() - dragStartPt.getY(); for( int i = 0; i < collSelectedViews.size(); i++ ) { uv = (UGenView) collSelectedViews.get( i ); pt = uv.getLocation(); uv.setLocation( new Point2D.Double( pt.getX() + dx, pt.getY() + dy )); } recalcBoundingBox(); dragStarted = false; repaint(); } dragStartPt = null; } public void mouseDragged( MouseEvent e ) { if( dragStartPt != null ) { if( !dragStarted ) { if( (Math.abs( e.getX() - dragStartPtScreen.getX() ) > 2) || (Math.abs( e.getY() - dragStartPtScreen.getY() ) > 2) ) { dragStarted = true; } else return; } dragCurrentPt = getVirtualPoint( e.getPoint() ); repaint(); } } public void mouseClicked( MouseEvent e ) { /* ignored */ } public void mouseMoved( MouseEvent e ) { /* ignored */ } public void mouseEntered( MouseEvent e ) { /* ignored */ } public void mouseExited( MouseEvent e ) { /* ignored */ } } private static class Wire implements Constants { private static final Paint pntScalarWire = new Color( 0x60, 0x60, 0x60 ); private static final Paint pntControlWire = new Color( 0x60, 0x60, 0xC0 ); private static final Paint pntAudioWire = new Color( 0xC0, 0x60, 0x60 ); private static final Paint pntDemandWire = new Color( 0x40, 0x80, 0x40 ); private final Paint pntWire; private Shape shpWire; private final Stroke strkWire = new BasicStroke( 2.0f ); private final UGenView outputUGen, inputUGen; private final int outputIndex, inputIndex; protected Wire( UGenView outputUGen, int outputIndex, UGenView inputUGen, int inputIndex ) { this.outputUGen = outputUGen; this.inputUGen = inputUGen; this.outputIndex = outputIndex; this.inputIndex = inputIndex; final Object rate = outputUGen.getUGen().getOutputRate( outputIndex ); if( rate == kScalarRate ) { pntWire = pntScalarWire; } else if( rate == kControlRate ) { pntWire = pntControlWire; } else if( rate == kAudioRate ) { pntWire = pntAudioWire; } else if( rate == kDemandRate ) { pntWire = pntDemandWire; } else { System.err.println( "Illegal rate : " + rate ); pntWire = pntScalarWire; } outputUGen.addOutletWire( this ); inputUGen.addInletWire( this ); recalcPositions(); } protected void recalcPositions() { shpWire = new Line2D.Double( outputUGen.getOutletLocation( outputIndex ), inputUGen.getInletLocation( inputIndex )); } protected void paint( Graphics2D g2 ) { final Stroke strkOrig = g2.getStroke(); // final AffineTransform atOrig = g2.getTransform(); // // g2.translate( -1, -1 ); // account for symmetric stroke width g2.setPaint( pntWire ); g2.setStroke( strkWire ); g2.draw( shpWire ); g2.setStroke( strkOrig ); // g2.setTransform( atOrig ); } } private static class UGenView implements Constants { private final UGen ugen; // private final UGenInfo ui; // private final String name; private final Shape shpContainer; private final Shape shpCons; private final Shape shpConsts; private final Shape shpToolTips; private final List collConsts = new ArrayList(); // elements = PositionedString private final List collConstBounds = new ArrayList(); // elements = Rectangle2D private final List collToolTips = new ArrayList(); // elements = PositionedString private final Rectangle2D bounds; private static final Stroke strkCons = new BasicStroke( 2.0f ); private static final Paint pntConst = new Color( 0x00, 0x00, 0x00, 0xC0 ); private static final Paint pntConstD = new Color( 0x00, 0x00, 0x00, 0x60 ); // private static final Paint pntToolTips = new GradientPaint( 0, 0, new Color( 0xFF, 0xFF, 0x00, 0xA0 ), // 0, 20, new Color( 0xFF, 0xFF, 0x00, 0x00 )); private static final Paint pntToolTips = new Color( 0xFF, 0xFF, 0x00, 0xB0 ); private static final Paint pntToolTipTxt = new Color( 0x00, 0x00, 0x00, 0xD8 ); private final Paint pntBackground; private final Paint pntBorder; private final Paint pntLabel; private final Paint pntBackgroundS; private final Paint pntBorderS; private final Paint pntLabelS; private final Paint pntBackgroundD; private final Paint pntBorderD; private final Paint pntLabelD; private final PositionedString label; private final Point2D[] inletLocations, outletLocations; private final List inletWires = new ArrayList(); // elements = Wire private final List outletWires = new ArrayList(); // elements = Wire private boolean selected = false; private boolean toolTips = false; private static final double minus90 = -Math.PI / 2; protected UGenView( UGen ugen, UGenInfo ui, String name, Rectangle2D bounds, FontMetrics fm, FontMetrics fm2 ) { this.ugen = ugen; // this.ui = ui; // this.name = name; this.bounds = bounds; inletLocations = new Point2D[ ugen.getNumInputs() ]; outletLocations = new Point2D[ ugen.getNumOutputs() ]; final GeneralPath gp = new GeneralPath(); final GeneralPath gp2 = new GeneralPath(); final Area a = new Area(); double x, x2, dx, y, y2, h; int r, g, b; UGenInput inp; String str; if( ugen.getRate() == kScalarRate ) { r = 0xC0; g = 0xC0; b = 0xC0; } else if( ugen.getRate() == kControlRate ) { r = 0x80; g = 0x80; b = 0xC0; } else if( ugen.getRate() == kAudioRate ) { r = 0xC0; g = 0x80; b = 0x80; } else if( ugen.getRate() == kDemandRate ) { r = 0x80; g = 0xC0; b = 0x80; } else { System.err.println( "Illegal rate : " + ugen.getRate() ); r = 0xC0; g = 0xC0; b = 0xC0; } pntBackground = new Color( r, g, b ); pntBorder = new Color( r >> 1, g >> 1, b >> 1 ); pntLabel = new Color( r >> 2, g >> 2, b >> 2 ); pntBackgroundS = new Color( r >> 1, g >> 1, b >> 1 ); pntBorderS = new Color( r >> 2, g >> 2, b >> 2 ); pntLabelS = Color.white; pntBackgroundD = new Color( r, g, b, 0x80 ); pntBorderD = new Color( r >> 1, g >> 1, b >> 1, 0x80 ); pntLabelD = new Color( r >> 2, g >> 2, b >> 2, 0x80 ); // inlets // x = SynthDefView.HPAD / 2 + 1 + bounds.getX(); x = SynthDefView.HPAD / 2 + 1; dx = (bounds.getWidth() - 2 - SynthDefView.HPAD) / (ugen.getNumInputs() - 1); // y = bounds.getY() + 1.0; y = 1.0; h = SynthDefView.VPAD / 4; for( int i = 0; i < ugen.getNumInputs(); i++ ) { inp = ugen.getInput( i ); y2 = y; if( inp instanceof Constant ) { y2 -= 2 + fm.getHeight(); str = SynthDefDiagram.formatConst( ((Constant) inp).getValue() ); collConsts.add( new PositionedString( str, x + 4, y2 + fm.getAscent() )); x2 = x + fm.stringWidth( str ); gp2.moveTo( (float) x2, (float) y2 ); gp2.lineTo( (float) x, (float) y2 ); gp2.lineTo( (float) x, (float) y ); collConstBounds.add( new Rectangle2D.Double( x, y2, x2 + 4 - x, y - y2 )); } gp.append( new Line2D.Double( x, y, x, y + h ), false ); inletLocations[ i ] = new Point2D.Double( x, y ); if( ui != null ) { str = ui.getArgNameForInput( ugen, i ); if( str != null ) { collToolTips.add( new PositionedString( str, x + fm2.getAscent() - 4, y2 - 4 )); x2 = fm2.stringWidth( str ) + 8; // a.add( new Area( new Rectangle2D.Double( x - 2, y2 - x2 - 2, fm2.getHeight(), x2 ))); a.add( new Area( new RoundRectangle2D.Double( x - 2.5, y2 - x2 - 2, fm2.getHeight(), x2, 6, 6 ))); } } x += dx; } // outlets // x = SynthDefView.HPAD / 2 + 1 + bounds.getX(); x = SynthDefView.HPAD / 2 + 1; dx = (bounds.getWidth() - 2 - SynthDefView.HPAD) / (ugen.getNumOutputs() - 1); // y = bounds.getY() + bounds.getHeight() - 1; y = bounds.getHeight() - 1; for( int i = 0; i < ugen.getNumOutputs(); i++ ) { gp.append( new Line2D.Double( x, y - h, x, y ), false ); outletLocations[ i ] = new Point2D.Double( x, y ); x += dx; } shpCons = gp; shpConsts = gp2; shpToolTips = a; // label = new PositionedString( ugen.getName(), SynthDefView.HPAD / 2 + bounds.getX(), // SynthDefView.VPAD / 2 + bounds.getY() + fm.getAscent() ); label = new PositionedString( name, SynthDefView.HPAD / 2, SynthDefView.VPAD / 2 + fm.getAscent() ); shpContainer = new Rectangle2D.Double( 0.0, 0.0, bounds.getWidth(), bounds.getHeight() ); // System.err.println( "# consts : " +collConsts.size() ); } protected void setSelected( boolean selected ) { this.selected = selected; } protected boolean isSelected() { return selected; } protected void showToolTips( boolean yesNo ) { this.toolTips = yesNo; } protected boolean isShowingToolTips() { return toolTips; } protected Point2D getLocation() { return new Point2D.Double( bounds.getX(), bounds.getY() ); } protected Rectangle2D getContainer() { return bounds.getBounds2D(); // a copy (?) } protected boolean contains( Point2D pt ) { return bounds.contains( pt ); } protected void setLocation( Point2D topLeft ) { bounds.setFrame( topLeft.getX(), topLeft.getY(), bounds.getWidth(), bounds.getHeight() ); for( int i = 0; i < inletWires.size(); i++ ) { ((Wire) inletWires.get( i )).recalcPositions(); } for( int i = 0; i < outletWires.size(); i++ ) { ((Wire) outletWires.get( i )).recalcPositions(); } } protected List getConstBounds() { final List result = new ArrayList( collConstBounds.size() ); Rectangle2D rect; for( int i = 0; i < collConstBounds.size(); i++ ) { rect = (Rectangle2D) collConstBounds.get( i ); result.add( new Rectangle2D.Double( rect.getX() + bounds.getX(), rect.getY() + bounds.getY(), rect.getWidth(), rect.getHeight() )); } return result; } protected Rectangle2D getBoundingBox() { final Rectangle2D result = getContainer(); final List constBounds = getConstBounds(); for( int i = 0; i < constBounds.size(); i++ ) { Rectangle2D.union( result, (Rectangle2D) constBounds.get( i ), result ); } return result; } protected void addInletWire( Wire wire ) { inletWires.add( wire ); } protected void addOutletWire( Wire wire ) { outletWires.add( wire ); } protected UGen getUGen() { return ugen; } protected Point2D getInletLocation( int index ) { return new Point2D.Double( inletLocations[ index ].getX() + bounds.getX(), inletLocations[ index ].getY() + bounds.getY() ); } protected Point2D getOutletLocation( int index ) { return new Point2D.Double( outletLocations[ index ].getX() + bounds.getX(), outletLocations[ index ].getY() + bounds.getY() ); } protected void paint( Graphics2D g2, FontMetrics fm, boolean dragging ) { final Stroke strkOrig = g2.getStroke(); final AffineTransform atOrig = g2.getTransform(); PositionedString pStr; g2.translate( bounds.getX(), bounds.getY() ); g2.setPaint( dragging ? pntBackgroundD : (selected ? pntBackgroundS : pntBackground) ); g2.fill( shpContainer ); g2.setPaint( dragging ? pntBorderD : (selected ? pntBorderS : pntBorder) ); g2.draw( shpContainer ); g2.setPaint( dragging ? pntLabelD : (selected ? pntLabelS : pntLabel) ); g2.drawString( label.str, (float) label.x, (float) label.y ); g2.setPaint( dragging ? pntConstD : pntConst ); for( int i = 0; i < collConsts.size(); i++ ) { pStr = (PositionedString) collConsts.get( i ); g2.drawString( pStr.str, (float) pStr.x, (float) pStr.y ); } g2.draw( shpConsts ); g2.setStroke( strkCons ); g2.draw( shpCons ); g2.setStroke( strkOrig ); g2.setTransform( atOrig ); } protected void paintToolTips( Graphics2D g2, FontMetrics fm ) { final Stroke strkOrig = g2.getStroke(); final AffineTransform atOrig = g2.getTransform(); PositionedString pStr; final AffineTransform atRecent; g2.translate( bounds.getX(), bounds.getY() ); atRecent = g2.getTransform(); g2.setPaint( pntToolTips ); g2.fill( shpToolTips ); g2.setPaint( pntToolTipTxt ); for( int i = 0; i < collToolTips.size(); i++ ) { pStr = (PositionedString) collToolTips.get( i ); g2.translate( pStr.x, pStr.y ); g2.rotate( minus90 ); g2.drawString( pStr.str, 0, 0 ); g2.setTransform( atRecent ); } g2.setStroke( strkOrig ); g2.setTransform( atOrig ); } } private static class PositionedString { protected final String str; protected final double x, y; protected PositionedString( String str, double x, double y ) { this.str = str; this.x = x; this.y = y; } } }